/* @flow */

import { hasOwn } from 'shared/util'
import { warn, hasSymbol } from '../util/index'
import { defineReactive, toggleObserving } from '../observer/index'

export function initProvide (vm: Component) {
  const provide = vm.$options.provide
  if (provide) {
    vm._provided = typeof provide === 'function'
      ? provide.call(vm)
      : provide
  }
}

export function initInjections (vm: Component) {
  //result是一个对象,对应的是子组件定义的key,组件组件定义的value
  //这里值得注意,祖先组件的value发生修改,并不能触发子组件的重新渲染.因为它没有定义成响应式
  //瞬间打脸,下面这个函数就是帮助定义响应式的.从这里可以看出,如果祖先定义的数据是一个对象,那么修改祖先对象值,
  /*

  provide() {
    return {
      getTestData: this.list  //这样定义子组件不会重新渲染.默认this.list =[],子组件存储的是[].这时候
      //就算父组件修改this.list = [1,2,3],子组件的还是指向原来的[].因此子组件不会触发渲染          
    }
  }

   provide() {
    return {
      getTestData(){  //这样定义子组件就能拿到最新的数据
        return this.list;
      }   
    }
  }

  这其实就是原生js里面的引用类型陷阱

  var ob = {
     a:[]
  }

  var c = {
    a:ob.a
  }; 

  ob.a = [1,2,3];

  ob是变了,但是c.a仍然等于[]

  因此你想让c.a与ob.a保持同步,关键点在于抓住ob,而不是ob.a.可以在ob定义一个函数getVal(){return ob.a}就可以了.

  */
  //实际测试中又不是上面所说的这样,只要provide定义的是一个对象,修改值子组件都获取不到最新的值.但是prvide定义的是一个
  //函数,函数里面返回一个对象值.那么在子组件里面接受到这个函数,拿到的就是最新的值

  //因此下面实现的defineReactive响应式没有啥意义啊,因为修改祖先的值子组件也不会重新渲染.唯一的意义是设置了代理，
  //通过vm.key可以拿到value

  const result = resolveInject(vm.$options.inject, vm)
  if (result) {
    toggleObserving(false)
    Object.keys(result).forEach(key => {
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production') {
        defineReactive(vm, key, result[key], () => {
          warn(
            `Avoid mutating an injected value directly since the changes will be ` +
            `overwritten whenever the provided component re-renders. ` +
            `injection being mutated: "${key}"`,
            vm
          )
        })
      } else {
        defineReactive(vm, key, result[key])
      }
    })
    toggleObserving(true)
  }
}

export function resolveInject (inject: any, vm: Component): ?Object {
  if (inject) {
    // inject is :any because flow is not smart enough to figure out cached
    const result = Object.create(null)
    const keys = hasSymbol //获取所有的keys
      ? Reflect.ownKeys(inject)
      : Object.keys(inject)

    for (let i = 0; i < keys.length; i++) {
      const key = keys[i]
      // #6574 in case the inject object is observed...
      if (key === '__ob__') continue
      const provideKey = inject[key].from
      let source = vm
      //这个循环是关键,一直往上一层找,直到找到包含key的祖先
      while (source) {
        if (source._provided && hasOwn(source._provided, provideKey)) {
          result[key] = source._provided[provideKey]
          break
        }
        source = source.$parent
      }
      //如果上面发现没找到,就走下面取默认值
      if (!source) {
        if ('default' in inject[key]) {
          const provideDefault = inject[key].default
          result[key] = typeof provideDefault === 'function'
            ? provideDefault.call(vm)
            : provideDefault
        } else if (process.env.NODE_ENV !== 'production') {
          warn(`Injection "${key}" not found`, vm)
        }
      }
    }
    return result
  }
}
